//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<vector.h>
///	@path	~/src/lib/math/
///	@date	2007/09/16
///	@desc	Vector, with 3 values packed for space coordinate.

#pragma once

#include "xeres.h"

#include "lib/math/math_ext.h"

namespace xeres {

	/*!
		\class		Vector
		\brief		Vector 3d encapsulation.
	*/
	template<
		typename _Ty
	> class Vector : public ArithmeticOp< _Ty >
	{
	public:

		// scalar type
		typedef _Ty _Scalar;

		// data
		union
		{
			// coordinate system access
			struct { _Scalar x , y , z; };

			// array access
			_Scalar f[3];
		};

		// accessible member
		static const size_t DIMENSION = 3;

	public:
		// default ctor
		Vector( void ) : x( 0 ) , y( 0 ) , z( 0 )
		{
		}
		// set ctor
		Vector( const _Scalar x , const _Scalar y , const _Scalar z ) : x( x ) , y( y ) , z( z )
		{
		}
		// copy ctor
		Vector( const Vector& v ) : x( v.x ) , y( v.y ) , z( v.z )
		{
		}
		// copy ctor
		template< typename _TOther > Vector( const Vector<_TOther>& val )
			: x( _Scalar(val.x) ) , y( _Scalar(val.y) ) , z( _Scalar(val.z) )
		{
		}
		// additional set ctor
		explicit Vector( const _Scalar s ) : x( s ) , y( s ) , z( s )
		{
		}
		// set from 3 any-type-values
		void set( const _Scalar sx , const _Scalar sy , const _Scalar sz )
		{
			x = sx;
			y = sy;
			z = sz;
		}
		// set from a data collection with subscription
		template<typename _TArray> void from_array( const _TArray& sa )
		{
			x = _Scalar(sa[0]);
			y = _Scalar(sa[1]);
			z = _Scalar(sa[2]);
		}
		// set to zero
		void set_zero( void )
		{
			x = y = z = 0;
		}
		// set from single value
		void single( const _Scalar s )
		{
			x = y = z = _Scalar(s);
		}
		// memory copy from a floating point array
		void copy( const _Scalar * s )
		{
			memcpy( f , s , sizeof(_Scalar) * DIMENSION );
		}
		// square sum of 2 components
		_Scalar square( void ) const
		{
			return x * x + y * y + z * z;
		}
		// length of vec
		_Scalar length( void ) const
		{
			return sqrt( square() );
		}
		// normalize vector
		void normalize( void )
		{
			TYPE_MUST_BE_FLOAT( _Scalar );
			_Scalar len = x * x + y * y + z * z;
			if( len > 0 )
			{
				len = recip_sqrt( len );
				x *= len;
				y *= len;
				z *= len;
			}
		}
		// normalize & return value
		Vector<_Scalar> normalized( void ) const
		{
			TYPE_MUST_BE_FLOAT( _Scalar );
			_Scalar len = x * x + y * y + z * z;
			if( len > 0 )
			{
				len = recip_sqrt( len );
				return Vector( x * len , y * len , z * len);
			}
			return *this;
		}
		// calculate the distance from this vector to another
		template< typename _TOther > _Scalar distance( const Vector<_TOther>& to ) const
		{
			TYPE_MUST_BE_FLOAT( _Scalar );
			TYPE_MUST_BE_FLOAT( _TOther );
			_Scalar a = x - _Scalar(to.x);
			_Scalar b = y - _Scalar(to.y);
			_Scalar c = z - _Scalar(to.z);
			return sqrt( a*a + b*b + c*c );
		}
		// calculate the dot product
		template<typename _TOther> _Scalar dot( const Vector<_TOther>& to ) const
		{
			TYPE_MUST_BE_FLOAT( _Scalar );
			TYPE_MUST_BE_FLOAT( _TOther );
			return x * _Scalar(to.x) + y * _Scalar(to.y) + z * _Scalar(to.z);
		}
		// calculate the cross product
		template<typename _T1 , typename _T2> void cross( const Vector<_T1>& a , const Vector<_T2>& b )
		{
			TYPE_MUST_BE_FLOAT( _Scalar );
			TYPE_MUST_BE_FLOAT( _T1 );
			TYPE_MUST_BE_FLOAT( _T2 );
			_Scalar _x = ( _Scalar(a[1]) * _Scalar(b[2]) - _Scalar(a[2]) * _Scalar(b[1]) );
			_Scalar _y = ( _Scalar(a[2]) * _Scalar(b[0]) - _Scalar(a[0]) * _Scalar(b[2]) );
			_Scalar _z = ( _Scalar(a[0]) * _Scalar(b[1]) - _Scalar(a[1]) * _Scalar(b[0]) );
			x = _x;
			y = _y;
			z = _z;
		}
		_Scalar theta( void ) const
		{
			TYPE_MUST_BE_FLOAT( _Scalar );
			return atan2( y , x );
		}
		_Scalar phi( void ) const
		{
			TYPE_MUST_BE_FLOAT( _Scalar );
			return atan2( x , z );
		}
		// comparison
		friend bool operator == ( const Vector& a , const Vector& b )
		{
			return ( a.x == b.x ) && ( a.y == b.y ) && ( a.z == b.z );
		}
		friend bool operator != ( const Vector& a , const Vector& b )
		{
			return ( a.x != b.x ) || ( a.y != b.y ) || ( a.z != b.z );
		}
		// subscription access
		_Scalar& operator [] ( const int index )
		{
			CHECK_INDEX( index , DIMENSION );
			return f[index];
		}
		const _Scalar& operator [] ( const int index ) const
		{
			CHECK_INDEX( index , DIMENSION );
			return f[index];
		}
		// assignment
		template< typename _TOther > const Vector& operator = ( const Vector<_TOther>& a )
		{
			f[0] = _Scalar(a.f[0]);
			f[1] = _Scalar(a.f[1]);
			f[2] = _Scalar(a.f[2]);
			return *this;
		}
		const Vector& operator = ( const _Scalar a )
		{
			x = y = z = a;
			return *this;
		}
		// operators
		friend Vector<float> operator - ( const Vector& v )
		{
			return Vector( -v.x , -v.y , -v.z );
		}
		const Vector& operator += ( const Vector& op )
		{
			x += _Scalar(op.x);
			y += _Scalar(op.y);
			z += _Scalar(op.z);
			return *this;
		}
		const Vector& operator += ( const _Scalar op )
		{
			x += op;
			y += op;
			z += op;
			return *this;
		}
		const Vector& operator -= ( const Vector& op )
		{
			x -= _Scalar(op.x);
			y -= _Scalar(op.y);
			z -= _Scalar(op.z);
			return *this;
		}
		const Vector& operator -= ( const _Scalar op )
		{
			x -= op;
			y -= op;
			z -= op;
			return *this;
		}
		const Vector& operator *= ( const Vector& op )
		{
			x *= _Scalar(op.x);
			y *= _Scalar(op.y);
			z *= _Scalar(op.z);
			return *this;
		}
		const Vector& operator *= ( const _Scalar op )
		{
			x *= op;
			y *= op;
			z *= op;
			return *this;
		}
		const Vector& operator /= ( const Vector& op )
		{
			x /= _Scalar(op.x);
			y /= _Scalar(op.y);
			z /= _Scalar(op.z);
			return *this;
		}
		const Vector& operator /= ( const _Scalar op )
		{
			_Scalar r = recip( op );
			x *= r;
			y *= r;
			z *= r;
			return *this;
		}

		// outside operators
		friend Vector operator + ( const Vector& a , const _Scalar op )
		{
			return Vector( a.x + op , a.y + op , a.z + op );
		}
		friend Vector operator - ( const Vector& a , const _Scalar op )
		{
			return Vector( a.x - op , a.y - op , a.z - op );
		}
		friend Vector operator * ( const Vector& a , const _Scalar op )
		{
			return Vector( a.x * op , a.y * op , a.z * op );
		}
		friend Vector operator / ( const Vector& a , const _Scalar op )
		{
			_Scalar r = recip( op );
			return Vector( a.x * r , a.y * r , a.z * r );
		}

		friend Vector operator + ( const Vector& a , const Vector& b )
		{
			return Vector( a.x + b.x , a.y + b.y , a.z + b.z );
		}
		friend Vector operator - ( const Vector& a , const Vector& b )
		{
			return Vector( a.x - b.x , a.y - b.y , a.z - b.z );
		}
		friend Vector operator * ( const Vector& a , const Vector& b )
		{
			return Vector( a.x * b.x , a.y * b.y , a.z * b.z );
		}
		friend Vector operator / ( const Vector& a , const Vector& b )
		{
			return Vector( a.x / b.x , a.y / b.y , a.z / b.z );
		}
	};

	// each component be the max of 2 input values.
	inline Vector<float> max( const Vector<float>& a , const Vector<float>& b )
	{
		return Vector<float>( max( a.x , b.x ) , max( a.y , b.y ) , max( a.z , b.z ) );
	}
	// each component be the min of 2 input values.
	inline Vector<float> min( const Vector<float>& a , const Vector<float>& b )
	{
		return Vector<float>( min( a.x , b.x ) , min( a.y , b.y ) , min( a.z , b.z ) );
	}
	// comparison with epsilon
	template<
		typename _Scalar
	> inline bool is_equal( const Vector<_Scalar>& a , const Vector<_Scalar>& b )
	{
		return ( is_equal( a.x , b.x ) && is_equal( a.y , b.y ) && is_equal( a.z , b.z ) );
	}
	template<
		typename _Scalar
	> inline Vector<_Scalar> clamp( const Vector<_Scalar>& value , const Vector<_Scalar>& minimum , const Vector<_Scalar>& maximum ) {
		return Vector<_Scalar>( clamp( value.x , minimum.x , maximum.x ) , clamp( value.y , minimum.y , maximum.y ) , clamp( value.z , minimum.z , maximum.z ) );
	}
	template<
		typename _Scalar
	> inline void clamp_ref( Vector<_Scalar>& value , const Vector<_Scalar>& minimum , const Vector<_Scalar>& maximum ) {
		clamp_ref( value.x , minimum.x , maximum.x );
		clamp_ref( value.y , minimum.y , maximum.y );
		clamp_ref( value.z , minimum.z , maximum.z );
	}
	template<
		typename _Scalar
	> inline float length( const Vector<_Scalar>& val ) {
		return val.length();
	}
	template<
		typename _Scalar
	> inline Vector<_Scalar> cross( const Vector<_Scalar>& a , const Vector<_Scalar>& b )
	{
		Vector<_Scalar> ret;
		ret.cross( a , b );
		return ret;
	}
	template<
		typename _Scalar
	> inline _Scalar distance( const Vector<_Scalar>& a , const Vector<_Scalar>& b )
	{
		return a.distance( b );
	}

} // namespace
